#pragma once

class CLabel : public CIdentifier
{
	friend class CLabels;

	int							m_nLineNum;
	int							m_nTokenLine;
	DWORD						m_dwOffset;
	int							m_nAsmID;
	bool						m_bIsScript				: 1;
	bool						m_bIsMission			: 1;
	bool						m_bIsStreamed			: 1;
	bool						m_bIsProcessed			: 1;	// has been compiled or decompiled
	bool						m_bIsReferenced			: 1;	//
	bool						m_bSkippedOnFirstPass	: 1;	// decompilation skipped by a GOTO, decompile later
	bool						m_bIsInternal			: 1;	// this is an internal label used for control structures
	bool						m_bInHex				: 1;	// label was created in hex (use relative pointers)
	bool						m_bInAsm				: 1;	// was declared in ASM struct (dont convert to offset)
	bool						m_bWaitingForDefinition	: 1;	// this identifier has been used as a label argument and we're waiting for the real label
	char	*					m_pRealStringPtr;
	CLabel	*					m_pPrevLabel;
	CLabel	*					m_pNextLabel;

	CLabel(const char *szName, int nLineNum) : CIdentifier(szName),
		m_dwOffset(0), m_nTokenLine(-1), m_nLineNum(nLineNum),
		m_pPrevLabel(nullptr), m_pNextLabel(nullptr),
		m_bInAsm(false),
		m_bInHex(false),
		m_bIsScript(false),
		m_bIsMission(false),
		m_bIsStreamed(false),
		m_bIsProcessed(false),
		m_bIsReferenced(false),
		m_bIsInternal(false),
		m_bSkippedOnFirstPass(false),
		m_bWaitingForDefinition(false)
	{
	}

public:
	inline CLabel * GetNext() const				{return m_pNextLabel;}
	inline CLabel * GetPrev() const				{return m_pPrevLabel;}
	inline int GetTokenLine() const				{return m_nTokenLine;}
	inline int GetLineNum()	const				{return m_nLineNum;}
	inline DWORD GetOffset() const				{return m_dwOffset;}
	inline void SetOffset(DWORD value)			{m_dwOffset = value;}
	inline void SetTokenLine(int value)			{m_nTokenLine = value;}
	inline void SetLineNum(int value)			{m_nLineNum = value;}
	
	inline void IsHex(bool b)					{m_bInHex = b;}
	inline void InAsm(int v)					{m_bInAsm = v != false;	m_nAsmID = v;}
	inline void IsInternal(bool b)				{m_bIsInternal = b;}
	inline void IsMission(bool b)				{m_bIsMission = b;}
	inline void IsProcessed(bool b)				{m_bIsProcessed = b;}
	inline void IsReferenced(bool b)			{m_bIsReferenced = b;}
	inline void IsScript(bool b)				{m_bIsScript = b;}
	inline void IsStreamed(bool b)				{m_bIsStreamed = b;}
	inline void WasSkippedOnFirstPass(bool b)	{m_bSkippedOnFirstPass = b;}
	inline void WaitingForDefinition(bool b)	{m_bWaitingForDefinition = b;}
	inline bool IsHex()	const					{return m_bInHex;}
	inline int	InAsm()	const					{return m_bInAsm ? m_nAsmID : false;}
	inline bool IsInternal() const				{return m_bIsInternal;}
	inline bool IsMission()	const				{return m_bIsMission;}
	inline bool IsProcessed() const				{return m_bIsProcessed;}
	inline bool IsReferenced() const			{return m_bIsReferenced;}
	inline bool IsScript() const				{return m_bIsScript;}
	inline bool IsStreamed() const				{return m_bIsStreamed;}
	inline bool WasSkippedOnFirstPass() const	{return m_bSkippedOnFirstPass;}
	inline bool WaitingForDefinition() const	{return m_bWaitingForDefinition;}
};

class CLabels
{
	static CList<CLabel>				s_pLabelList;					// binary tree of label name hashes
	static CLListEx<CLabel*>			s_pLabelLinks;					// linked list of labels
	//static map<int,CLabel*>			s_pLabelMap;
	static int							s_nNumLabels;

public:
	static bool LoadFromFile(const char *szFilePath);

	static CLabel * Add(const char *szName, int nLineNum)
	{
		CLabel * pLabel = new CLabel(szName, nLineNum);
		s_pLabelLinks.Add(pLabel);
		++s_nNumLabels;
		return pLabel;
	}

	static void IndexName(CLabel *pLabel)
	{
		s_pLabelList.Add(pLabel->GetHash(), pLabel);
	}

	static CLabel * GetByName(const char * szName)
	{
		int hash = GenerateHash(szName);
		auto pLabel = s_pLabelList.Get(hash);
		while(pLabel && !pLabel->IsNamed(szName, hash)) pLabel = s_pLabelList.Next();
		return pLabel;
	}

	static void Reset()
	{
		s_pLabelList.Clear();
		s_pLabelLinks.Clear();
		s_nNumLabels = 0;
	}
};